#include <compiler.h>

/********************************** (C) COPYRIGHT *******************************
 * File Name          : USBHOST.C
 * Author             : WCH
 * Version            : V1.1
 * Date               : 2018/02/28
 * Description        : CH549 USB 主机接口函数
 *******************************************************************************/

#include <CH549_DEBUG.h>
#include <CH549_sdcc.h>
#include <HID.H>
#include <USBHOST.H>

/*******************************************************************************
 * Function Name  : DisableRootHubPort( )
 * Description    : 关闭HUB端口
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void DisableRootHubPort() {
    ThisUsbDev.DeviceStatus = ROOT_DEV_DISCONNECT;
    ThisUsbDev.DeviceAddress = 0x00;
    ThisUsbDev.HIDEndPoint = 0;
}
/*******************************************************************************
* Function Name  : AnalyzeRootHub(void)
* Description    : 分析ROOT-HUB状态,处理ROOT-HUB端口的设备插拔事件
                   如果设备拔出,函数中调用DisableRootHubPort()函数,将端口关闭,插入事件,置相应端口的状态位
* Input          : None
* Output         : None
* Return         : 返回ERR_SUCCESS为没有情况,返回ERR_USB_CONNECT为检测到新连接,返回ERR_USB_DISCON为检测到断开
*******************************************************************************/
UINT8 AnalyzeRootHub(void) {
    UINT8 s;
    s = ERR_SUCCESS;
    if (USB_MIS_ST & bUMS_DEV_ATTACH) {                    // 设备存在
        if (ThisUsbDev.DeviceStatus == ROOT_DEV_DISCONNECT // 检测到有设备插入
            || (UHOST_CTRL & bUH_PORT_EN) == 0x00) {       // 检测到有设备插入,但尚未允许,说明是刚插入
            DisableRootHubPort();                          // 关闭端口
            // ThisUsbDev.DeviceSpeed = USB_HUB_ST & bUHS_DM_LEVEL ? 0 : 1;
            ThisUsbDev.DeviceStatus = ROOT_DEV_CONNECTED; // 置连接标志
            printf("USB dev in\n");
            s = ERR_USB_CONNECT;
        }
    } else if (ThisUsbDev.DeviceStatus >= ROOT_DEV_CONNECTED) { // 检测到设备拔出
        DisableRootHubPort();                                   // 关闭端口
        printf("USB dev out\n");
        if (s == ERR_SUCCESS)
            s = ERR_USB_DISCON;
    }
    //	UIF_DETECT = 0;                                                            // 清中断标志
    return (s);
}
/*******************************************************************************
 * Function Name  : SetHostUsbAddr
 * Description    : 设置USB主机当前操作的USB设备地址
 * Input          : UINT8 addr
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SetHostUsbAddr(UINT8 addr) {
    USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | addr & 0x7F;
}

/*******************************************************************************
 * Function Name  : SetUsbSpeed
 * Description    : 设置当前USB速度
 * Input          : UINT8 FullSpeed
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SetUsbSpeed(UINT8 FullSpeed) {
    if (FullSpeed) // 全速
    {
        USB_CTRL &= ~bUC_LOW_SPEED;  // 全速
        UH_SETUP &= ~bUH_PRE_PID_EN; // 禁止PRE PID
    } else {
        USB_CTRL |= bUC_LOW_SPEED; // 低速
    }
}

/*******************************************************************************
 * Function Name  : ResetRootHubPort( )
 * Description    : 检测到设备后,复位总线,为枚举设备准备,设置为默认为全速
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void ResetRootHubPort() {
    ThisUsbDev.UsbDevEndp0Size = DEFAULT_ENDP0_SIZE; // USB设备的端点0的最大包尺寸
    SetHostUsbAddr(0x00);
    UHOST_CTRL &= ~bUH_PORT_EN;                               // 关掉端口
    SetUsbSpeed(1);                                           // 默认为全速
    UHOST_CTRL = UHOST_CTRL & ~bUH_LOW_SPEED | bUH_BUS_RESET; // 默认为全速,开始复位
    mDelaymS(20);                                             // 复位时间10mS到20mS
    UHOST_CTRL = UHOST_CTRL & ~bUH_BUS_RESET;                 // 结束复位
    while ((USB_MIS_ST & bUMS_DEV_ATTACH) == 0)
        ;
    mDelaymS(1);
    UIF_DETECT = 0; // 清中断标志
}
/*******************************************************************************
 * Function Name  : EnableRootHubPort( )
 * Description    : 使能ROOT-HUB端口,相应的bUH_PORT_EN置1开启端口,设备断开可能导致返回失败
 * Input          : None
 * Output         : None
 * Return         : 返回ERR_SUCCESS为检测到新连接,返回ERR_USB_DISCON为无连接
 *******************************************************************************/
UINT8 EnableRootHubPort() {
    // if ( ThisUsbDev.DeviceStatus < ROOT_DEV_CONNECTED )
    ThisUsbDev.DeviceStatus = ROOT_DEV_CONNECTED;
    if (USB_MIS_ST & bUMS_DEV_ATTACH) {           // 有设备
        if ((UHOST_CTRL & bUH_PORT_EN) == 0x00) { // 尚未使能
            ThisUsbDev.DeviceSpeed = USB_MIS_ST & bUMS_DM_LEVEL ? 0 : 1;
            if (ThisUsbDev.DeviceSpeed == 0)
                UHOST_CTRL |= bUH_LOW_SPEED; // 低速
        }
        USB_CTRL |= bUC_DMA_EN; // 启动USB主机及DMA,在中断标志未清除前自动暂停
        UH_SETUP = bUH_SOF_EN;
        UHOST_CTRL |= bUH_PORT_EN; // 使能HUB端口
        return (ERR_SUCCESS);
    }
    return (ERR_USB_DISCON);
}

/*******************************************************************************
 * Function Name  : SelectHubPort
 * Description    : 选定需要操作的HUB口
 * Input          : UINT8 HubPortIndex 选择操作指定的ROOT-HUB端口的外部HUB的指定端口
 * Output         : None
 * Return         : None
 *******************************************************************************/
void SelectHubPort() {
    SetHostUsbAddr(ThisUsbDev.DeviceAddress); // 设置USB主机当前操作的USB设备地址
    SetUsbSpeed(ThisUsbDev.DeviceSpeed);      // 设置USB设备的速度
}

/*******************************************************************************
* Function Name  : WaitUSB_Interrupt
* Description    : 等待USB中断
* Input          : None
* Output         : None
* Return         : 返回ERR_SUCCESS 数据接收或者发送成功
                   ERR_USB_UNKNOWN 数据接收或者发送失败
*******************************************************************************/
UINT8 WaitUSB_Interrupt(void) {
    UINT16 i;
    for (i = WAIT_USB_TOUT_200US; i != 0 && UIF_TRANSFER == 0; i--) {
        ;
    }
    return (UIF_TRANSFER ? ERR_SUCCESS : ERR_USB_UNKNOWN);
}
/*******************************************************************************
* Function Name  : USBTransact
* Description    : 令牌包传输,输入目的端点地址/PID令牌,同步标志,以20uS为单位的NAK重试总时间(0则不重试,0xFFFF无限重试),返回0成功,超时/出错重试
                   本子程序着重于易理解,而在实际应用中,为了提供运行速度,应该对本子程序代码进行优化
* Input          : UINT8 endp_pid 令牌和地址  endp_pid: 高4位是token_pid令牌, 低4位是端点地址
                   UINT8 tog      同步标志
                   UINT16 timeout 超时时间
* Output         : None
* Return         : ERR_USB_UNKNOWN 超时，可能硬件异常
                   ERR_USB_DISCON  设备断开
                   ERR_USB_CONNECT 设备连接
                   ERR_SUCCESS     传输完成
*******************************************************************************/
extern __xdata __at(0x0015A) UINT8 USBTransact_endp_pid;
extern __xdata __at(0x0015B) UINT8 USBTransact_tog;
extern __xdata __at(0x0015C) UINT16 USBTransact_timeout;
UINT8 USBTransact() {
    UINT8 TransRetry;
    // #define	TransRetry	UEP0_T_LEN	                                               // 节约内存
    UINT8 s, r;
    UINT16 i;
    UH_RX_CTRL = UH_TX_CTRL = USBTransact_tog;
    TransRetry = 0;

    do {
        UH_EP_PID = USBTransact_endp_pid; // 指定令牌PID和目的端点号
        UIF_TRANSFER = 0;                 // 允许传输
        // s = WaitUSB_Interrupt( );
        for (i = WAIT_USB_TOUT_200US; i != 0 && UIF_TRANSFER == 0; i--)
            ;
        UH_EP_PID = 0x00; // 停止USB传输
                          //	if ( s != ERR_SUCCESS ) return( s );  // 中断超时,可能是硬件异常
        if (UIF_TRANSFER == 0)
            return (ERR_USB_UNKNOWN);
        if (UIF_DETECT) {         // USB设备插拔事件
                                  //			mDelayuS( 200 );                                                       // 等待传输完成
            UIF_DETECT = 0;       // 清中断标志
            s = AnalyzeRootHub(); // 分析ROOT-HUB状态
            if (s == ERR_USB_CONNECT)
                FoundNewDev = 1;
            if (ThisUsbDev.DeviceStatus == ROOT_DEV_DISCONNECT)
                return (ERR_USB_DISCON); // USB设备断开事件
            if (ThisUsbDev.DeviceStatus == ROOT_DEV_CONNECTED)
                return (ERR_USB_CONNECT); // USB设备连接事件
            mDelayuS(200);                // 等待传输完成
        }
        if (UIF_TRANSFER) { // 传输完成
            if (U_TOG_OK)
                return (ERR_SUCCESS);
            r = USB_INT_ST & MASK_UIS_H_RES; // USB设备应答状态
            if (r == USB_PID_STALL)
                return (r | ERR_USB_TRANSFER);
            if (r == USB_PID_NAK) {
                if (USBTransact_timeout == 0)
                    return (r | ERR_USB_TRANSFER);
                if (USBTransact_timeout < 0xFFFF)
                    USBTransact_timeout--;
                --TransRetry;
            } else
                switch (USBTransact_endp_pid >> 4) {
                case USB_PID_SETUP:
                case USB_PID_OUT:
                    //					if ( U_TOG_OK ) return( ERR_SUCCESS );
                    //					if ( r == USB_PID_ACK ) return( ERR_SUCCESS );
                    //					if ( r == USB_PID_STALL || r == USB_PID_NAK ) return( r | ERR_USB_TRANSFER );
                    if (r)
                        return (r | ERR_USB_TRANSFER); // 不是超时/出错,意外应答
                    break;                             // 超时重试
                case USB_PID_IN:
                    //					if ( U_TOG_OK ) return( ERR_SUCCESS );
                    //					if ( tog ? r == USB_PID_DATA1 : r == USB_PID_DATA0 ) return( ERR_SUCCESS );
                    //					if ( r == USB_PID_STALL || r == USB_PID_NAK ) return( r | ERR_USB_TRANSFER );
                    if (r == USB_PID_DATA0 && r == USB_PID_DATA1) { // 不同步则需丢弃后重试
                    } // 不同步重试
                    else if (r)
                        return (r | ERR_USB_TRANSFER); // 不是超时/出错,意外应答
                    break;                             // 超时重试
                default:
                    return (ERR_USB_UNKNOWN); // 不可能的情况
                    break;
                }
        } else {               // 其它中断,不应该发生的情况
            USB_INT_FG = 0xFF; /* 清中断标志 */
        }
        mDelayuS(15);
    } while (++TransRetry < 3);

    return (ERR_USB_TRANSFER); // 应答超时
}
/*******************************************************************************
* Function Name  : USBSetupstTransfer
* Description    : 执行SETUP事务,8字节请求码在pSetupReq中,DataBuf为可选的收发缓冲区
* Input          : PUINT8X DataBuf 如果需要接收和发送数据,那么DataBuf需指向有效缓冲区用于存放后续数据
                   PUINT8 RetLen  实际成功收发的总长度保存在RetLen指向的字节变量中
* Output         : None
* Return         : ERR_USB_BUF_OVER IN状态阶段出错
                   ERR_SUCCESS     数据交换成功
                   其他错误状态
*******************************************************************************/
UINT8 USBSetupstTransfer(PUINT8X DataBuf, PUINT8X RetLen) {
    UINT16 RemLen = 0;
    UINT8 s, RxLen, RxCnt, TxCnt;

    mDelayuS(200);
    if (RetLen) {
        *RetLen = 0; // 实际成功收发的总长度
    }

    // SETUP阶段(setup stage),200mS超时
    UH_TX_LEN = sizeof(USB_SETUP_REQ);

    USBTransact_endp_pid = USB_PID_SETUP << 4 | 0x00;
    USBTransact_tog = 0x00;
    USBTransact_timeout = 200000 / 20;
    s = USBTransact();

    if (s != ERR_SUCCESS) {
        printf("USB_SETUP_REQ : x%02x \n", s);
        return (s);
    }

    // 数据阶段(data stage),200mS超时
    UH_RX_CTRL = UH_TX_CTRL = bUH_R_TOG | bUH_R_AUTO_TOG | bUH_T_TOG | bUH_T_AUTO_TOG; // 默认DATA1
    UH_TX_LEN = 0x01;                                                                  // 默认无数据故状态阶段为IN
    RemLen = (pSetupReq->wLengthH << 8) | (pSetupReq->wLengthL);
    if (RemLen && DataBuf) // 需要收发数据
    {
        if (pSetupReq->bRequestType & USB_REQ_TYP_IN) // 收
        {
            while (RemLen) {
                mDelayuS(200);
                USBTransact_endp_pid = USB_PID_IN << 4 | 0x00;
                USBTransact_tog = UH_RX_CTRL;
                USBTransact_timeout = 200000 / 20;
                s = USBTransact(); // IN数据
                if (s != ERR_SUCCESS) {
                    // 如果这里出错，InitUSB_Host() 的 UH_TX_DMA 是否设错
                    printf("USBSetupstTransfer 2 : x%02x \n", s);
                    return (s);
                }
                RxLen = USB_RX_LEN < RemLen ? USB_RX_LEN : RemLen;
                RemLen -= RxLen;
                if (RetLen) {
                    *RetLen += RxLen; // 实际成功收发的总长度
                }
                //              memcpy( DataBuf, RxBuffer, RxLen );
                //              DataBuf += RxLen;
                for (RxCnt = 0; RxCnt != RxLen; RxCnt++) {
                    *DataBuf = RxBuffer[RxCnt];
                    DataBuf++;
                }
                // if ( USB_RX_LEN == 0 || ( USB_RX_LEN & ( ThisUsbDev.UsbDevEndp0Size - 1 ) ) )        // 短包
                if (USB_RX_LEN == 0) {
                    break;
                }
            }
            UH_TX_LEN = 0x00; // 状态阶段为OUT
        } else                // 发
        {
            while (RemLen) {
                mDelayuS(200);
                UH_TX_LEN = RemLen >= ThisUsbDev.UsbDevEndp0Size ? ThisUsbDev.UsbDevEndp0Size : RemLen;
                //              memcpy( TxBuffer, DataBuf, UH_TX_LEN );
                //              DataBuf += UH_TX_LEN;
                if (DataBuf[1] == 0x09) // HID类命令处理
                {
                    Set_Port = Set_Port ^ 1;
                    *DataBuf = Set_Port;
                    printf("SET_PORT  %02X  %02X ", (UINT16)(*DataBuf), (UINT16)(Set_Port));
                }
                for (TxCnt = 0; TxCnt != UH_TX_LEN; TxCnt++) {
                    TxBuffer[TxCnt] = *DataBuf;
                    DataBuf++;
                }
                USBTransact_endp_pid = USB_PID_OUT << 4 | 0x00;
                USBTransact_tog = UH_TX_CTRL;
                USBTransact_timeout = 200000 / 20;
                s = USBTransact(); // OUT数据
                if (s != ERR_SUCCESS) {
                    printf("USBSetupstTransfer 3 : x%02x \n", s);
                    return (s);
                }
                RemLen -= UH_TX_LEN;
                if (RetLen) {
                    *RetLen += UH_TX_LEN; // 实际成功收发的总长度
                }
            }
            // UH_TX_LEN = 0x01;                                                     // 状态阶段为IN
        }
    }

    // STATUS阶段(status stage)
    mDelayuS(200);
    USBTransact_endp_pid = (UH_TX_LEN ? USB_PID_IN << 4 | 0x00 : USB_PID_OUT << 4 | 0x00);
    USBTransact_tog = bUH_R_TOG | bUH_T_TOG;
    USBTransact_timeout = 200000 / 20;
    s = USBTransact();
    if (s != ERR_SUCCESS) {
        printf("USBSetupstTransfer 4 :x%02x\n", s);
        return (s);
    }

    return (s);
}
/*******************************************************************************
 * Function Name  : CopySetupReqPkg
 * Description    : 复制控制传输的请求包
 * Input          : PUINT8C pReqPkt 控制请求包地址
 * Output         : None
 * Return         : None
 *******************************************************************************/
void CopySetupReqPkg(PUINT8C pReqPkt) // 复制控制传输的请求包
{
    UINT8 i;
    for (i = 0; i != sizeof(USB_SETUP_REQ); i++) {
        ((PUINT8X)pSetupReq)[i] = *pReqPkt;
        pReqPkt++;
    }
}
/*******************************************************************************
* Function Name  : CtrlGetDeviceDescr
* Description    : 获取设备描述符,返回在Com_Buffer中
* Input          : None
* Output         : None
* Return         : ERR_USB_BUF_OVER 描述符长度错误
                   ERR_SUCCESS      成功
                   其他
*******************************************************************************/
UINT8 CtrlGetDevDescr(void) {
    UINT8 s;
    ThisUsbDev.UsbDevEndp0Size = DEFAULT_ENDP0_SIZE;
    CopySetupReqPkg(SetupGetDevDescr);

    s = USBSetupstTransfer(Com_Buffer, &Com_BufferLen); // 执行控制传输
    if (s != ERR_SUCCESS) {
        return (s);
    }

    ThisUsbDev.UsbDevEndp0Size = ((PXUSB_DEV_DESCR)Com_Buffer)->bMaxPacketSize0; // 端点0最大包长度,这是简化处理,正常应该先获取前8字节后立即更新UsbDevEndp0Size再继续
    if (Com_BufferLen < ((PUSB_SETUP_REQ)SetupGetDevDescr)->wLengthL) {
        return (ERR_USB_BUF_OVER); // 描述符长度错误
    }
    return (ERR_SUCCESS);
}
/*******************************************************************************
* Function Name  : CtrlGetConfigDescr
* Description    : 获取配置描述符,返回在Com_Buffer中
* Input          : None
* Output         : None
* Return         : ERR_USB_BUF_OVER 描述符长度错误
                   ERR_SUCCESS      成功
                   其他
*******************************************************************************/
UINT8 CtrlGetConfigDescr(void) {
    UINT8 s;

    // 先获取配置描述符的总长度
    CopySetupReqPkg(SetupGetCfgDescr);
    s = USBSetupstTransfer(Com_Buffer, &Com_BufferLen); // 执行控制传输
    if (s != ERR_SUCCESS) {
        return (s);
    }

    // 重新获取配置描述符（最多256字节）
    Com_BufferLen = ((PXUSB_CFG_DESCR)Com_Buffer)->wTotalLengthL;
    CopySetupReqPkg(SetupGetCfgDescr);
    pSetupReq->wLengthL = Com_BufferLen;                // 完整配置描述符的总长度
    s = USBSetupstTransfer(Com_Buffer, &Com_BufferLen); // 执行控制传输
    if (s != ERR_SUCCESS) {
        return (s);
    }
    return (ERR_SUCCESS);
}
/*******************************************************************************
* Function Name  : CtrlSetUsbAddress
* Description    : 设置USB设备地址
* Input          : UINT8 addr 设备地址
* Output         : None
* Return         : ERR_SUCCESS      成功
                   其他
*******************************************************************************/
UINT8 CtrlSetAddress(UINT8 addr) {
    UINT8 s;
    CopySetupReqPkg(SetupSetAddr);
    pSetupReq->wValueL = addr;                   // USB设备地址
    s = USBSetupstTransfer(NULL, (PUINT8X)NULL); // 执行控制传输
    if (s != ERR_SUCCESS) {
        return (s);
    }
    SetHostUsbAddr(addr); // 设置USB主机当前操作的USB设备地址
    mDelaymS(10);         // 等待USB设备完成操作
    return (ERR_SUCCESS);
}
/*******************************************************************************
* Function Name  : CtrlSetUsbConfig
* Description    : 设置USB设备配置
* Input          : UINT8 cfg       配置值
* Output         : None
* Return         : ERR_SUCCESS      成功
                   其他
*******************************************************************************/
UINT8 CtrlSetConfig(UINT8 cfg) {
    CopySetupReqPkg(SetupSetConfig);
    pSetupReq->wValueL = cfg;                         // USB设备配置
    return (USBSetupstTransfer(NULL, (PUINT8X)NULL)); // 执行控制传输
}
/*******************************************************************************
* Function Name  : CtrlClearEndpStall
* Description    : 清除端点STALL
* Input          : UINT8 endp       端点地址
* Output         : None
* Return         : ERR_SUCCESS      成功
                   其他
*******************************************************************************/
UINT8 CtrlClearEndpStall(UINT8 endp) {
    CopySetupReqPkg(SetupClrEndpStall);               // 清除端点的错误
    pSetupReq->wIndexL = endp;                        // 端点地址
    return (USBSetupstTransfer(NULL, (PUINT8X)NULL)); // 执行控制传输
}

/*******************************************************************************
* Function Name  : CtrlSetUsbIntercace
* Description    : 设置USB设备接口
* Input          : UINT8 cfg       配置值
* Output         : None
* Return         : ERR_SUCCESS      成功
                   其他
*******************************************************************************/
UINT8 CtrlSetIntercace(UINT8 cfg) {
    CopySetupReqPkg(SetupSetInterface);
    pSetupReq->wValueL = cfg;                         // USB设备配置
    return (USBSetupstTransfer(NULL, (PUINT8X)NULL)); // 执行控制传输
}

/*******************************************************************************
* Function Name  : CtrlHIDSetIdle
* Description    : 设置USB设备接口
* Input          : UINT8 cfg       配置值
* Output         : None
* Return         : ERR_SUCCESS      成功
                   其他
*******************************************************************************/
void CtrlHIDSetIdle(UINT8 infc) {
    UINT8 s;

    // 设置 HID空闲不发包
    CopySetupReqPkg(SetupHIDSetIdle);
    pSetupReq->wIndexL = infc;
    s = USBSetupstTransfer(Com_Buffer, &Com_BufferLen); // 执行控制传输
    printf("SetupHIDSetIdle result: x%02x \n", s);
}
/*******************************************************************************
* Function Name  : CtrlHIDGetReportDescr
* Description    : 获取HID设备报表描述符,返回在Com_Buffer中
* Input          : None
* Output         : None
* Return         : ERR_SUCCESS 成功
                   其他        错误
*******************************************************************************/
UINT8 CtrlHIDGetReportDescr() {
    UINT8 s;

    // 获取报表描述符
    CopySetupReqPkg(SetupHIDGetReportDescr);
    pSetupReq->wIndexL = ThisUsbDev.HIDbIfcNum;
    pSetupReq->wLengthL = ThisUsbDev.HIDDescriptorLength;
    s = USBSetupstTransfer(Com_Buffer, &Com_BufferLen); // 执行控制传输

    return (ERR_SUCCESS);
}

/*******************************************************************************
 * Function Name  : AnalyzeCfgDescr
 * Description    : 从描述符中分析出HID中断端点的地址,如果HubPortIndex是0保存到ROOTHUB，如果是非零值则保存到HUB下结构体
 * Input          : PUINT8X buf ： 待分析数据缓冲区地址 HubPortIndex：0表示根HUB，非0表示外部HUB下的端口号
 * Output         : cfg —— SetConfiguration()用此参数激活配置
 *                  ifc —— 设备的接口个数
 *
 * Return         : 端点数
 *******************************************************************************/
UINT8 AnalyzeCfgDescr(PUINT8X buf, UINT8 *cfg, UINT8 *ifc) {
    UINT8 i, s, l;
    s = 0;
    __bit isKBorMSInterface; // 是否是键盘或鼠标的接口描述符

    UINT8 j;

    ThisUsbDev.HIDEndPoint = 0; // 清空端点
    ThisUsbDev.DeviceType = USB_DEV_CLASS_RESERVED;
    ThisUsbDev.HIDbInterval = 0;

    isKBorMSInterface = 0;

    for (i = 0; i < ((PXUSB_CFG_DESCR)buf)->wTotalLengthL; i += l) // 一般设备描述符位数<256，所以只遍历低8位
    {
        l = buf[i]; // bLength

        // 打印配置描述符
        for (j = 0; j < l; j++) {
            printf("x%02X ", buf[i + j]);
        }

        if (buf[i + 1] == USB_DESCR_TYP_CONFIG) {
            // 配置描述符
            *cfg = buf[i + 5]; // bConfigurationValue
            *ifc = buf[i + 4]; // bNumInterfaces
        } else if (buf[i + 1] == USB_DESCR_TYP_INTERF) {

            // 接口描述符
            if (buf[i + 5] == 0x03 && buf[i + 7] == 0x01) {
                // 键盘
                if (ps2_dev) {
                    // 键盘模式下才识别
                    ThisUsbDev.DeviceType = DEV_TYPE_KEYBOARD;
                    ThisUsbDev.HIDbIfcNum = buf[i + 2];
                    isKBorMSInterface = 1;
                } else {
                    isKBorMSInterface = 0;
                }
                printf("\t\t-- Keyboard");

            } else if (buf[i + 5] == 0x03 && buf[i + 7] == 0x02) {
                // 鼠标
                if (!ps2_dev) {
                    // 鼠标模式下才识别
                    ThisUsbDev.DeviceType = DEV_TYPE_MOUSE;
                    ThisUsbDev.HIDbIfcNum = buf[i + 2];
                    isKBorMSInterface = 1;
                } else {
                    isKBorMSInterface = 0;
                }

                printf("\t\t-- Mouse");

            } else {
                isKBorMSInterface = 0;

                printf("\t\t-- ??");
            }

        } else if (buf[i + 1] == USB_DESCR_TYP_HID) {
            // HID类描述符
            if (isKBorMSInterface) {
                ThisUsbDev.HIDDescriptorLength = buf[i + 7];
            }

            // bCountryCode
            if (buf[i + 4] == 0x21) {
                keyboard_mode = 1; // 日语键盘
            } else {
                keyboard_mode = 0; // 美式键盘
            }

            printf("\t\t-- bDescriptorType:");
            switch (buf[i + 6]) {
            case 0x21:
                printf("HID");
                break;
            case 0x22:
                printf("Report");
                break;
            case 0x23:
                printf("Physical");
                break;
            }
            printf("(x%02x)", buf[i + 6]);
            printf("\t\t-- wDescriptorLength:%d", *((UINT16 *)&buf[i + 7]));

        } else if (buf[i + 1] == USB_DESCR_TYP_ENDP) {
            // 端点描述符——认为只有一个中断IN端点

            if (isKBorMSInterface &&                                        // 是键盘或鼠标
                (buf[i + 2] & USB_ENDP_DIR_MASK) &&                         // 是IN端点
                (buf[i + 3] & USB_ENDP_TYPE_MASK == USB_ENDP_TYPE_INTER)) { // 是中断端点

                ThisUsbDev.HIDEndPoint = buf[i + 2] & USB_ENDP_ADDR_MASK; // 中断端点的地址
                ThisUsbDev.HIDbInterval = buf[i + 6];                     // bInterval 中断轮询间隔（ms）
                s++;

                printf("\t\t\t-- IN endpoint,interrupt, end point addr:x%02x, bInterval:%d", ThisUsbDev.HIDEndPoint, ThisUsbDev.HIDbInterval);
            }
        }

        printf("\n");
    }
    return (s);
}

/*******************************************************************************
 * Function Name  : InitRootDevice
 * Description    : 初始化指定ROOT-HUB端口的USB设备
 * Input          : UINT8 RootHubIndex 指定端口，内置HUB端口号0/1
 * Output         : None
 * Return         :
 *******************************************************************************/
UINT8 InitRootDevice(void) {
    UINT8 t, i, s, cfg, dv_cls, ifc;
    UINT8 touchaoatm = 0;
    t = 0;

    printf("Reset USB Port\n");

USBDevEnum:
    for (i = 0; i < t; i++) {
        mDelaymS(100);
        if (t > 3)
            return (s);

        printf("InitRootDevice retry...\n");
    }
    ResetRootHubPort();              // 检测到设备后,复位相应端口的USB总线
    for (i = 0, s = 0; i < 100; i++) // 等待USB设备复位后重新连接,100mS超时
    {
        mDelaymS(1);
        if (EnableRootHubPort() == ERR_SUCCESS) // 使能ROOT-HUB端口
        {
            i = 0;
            s++; // 计时等待USB设备连接后稳定
            if (s > (20 + t)) {
                break; // 已经稳定连接15mS
            }
        }
    }
    if (i) // 复位后设备没有连接
    {
        DisableRootHubPort();

        printf("Disable usb port because of disconnect\n");

        //         return( ERR_USB_DISCON );
    }
    SelectHubPort();

    printf("GetDevDescr: \n");

    s = CtrlGetDevDescr(); // 获取设备描述符
    if (s == ERR_SUCCESS) {

        for (i = 0; i < ((PUSB_SETUP_REQ)SetupGetDevDescr)->wLengthL; i++) {
            printf("x%02X ", (UINT16)(Com_Buffer[i]));
        }
        printf("\n"); // 显示出描述符

        ThisUsbDev.DeviceVID = (((UINT16)((PXUSB_DEV_DESCR)Com_Buffer)->idVendorH) << 8) + ((PXUSB_DEV_DESCR)Com_Buffer)->idVendorL; // 保存VID PID信息
        ThisUsbDev.DevicePID = (((UINT16)((PXUSB_DEV_DESCR)Com_Buffer)->idProductH) << 8) + ((PXUSB_DEV_DESCR)Com_Buffer)->idProductL;
        dv_cls = ((PXUSB_DEV_DESCR)Com_Buffer)->bDeviceClass; // 设备类代码

        if (dv_cls) {
            // HID设备的话，设备类代码必为0x00，所以不为0则返回错误
            return (ERR_USB_UNSUPPORT);
        }

        s = CtrlSetAddress(((PUSB_SETUP_REQ)SetupSetAddr)->wValueL); // 设置USB设备地址,加上RootHubIndex可以保证2个HUB端口分配不同的地址
        if (s == ERR_SUCCESS) {
            ThisUsbDev.DeviceAddress = ((PUSB_SETUP_REQ)SetupSetAddr)->wValueL; // 保存USB地址

            printf("GetCfgDescr: \n");

            s = CtrlGetConfigDescr(); // 获取配置描述符（集合：包括配置描述符，接口描述符，类描述符，端点描述符等）
            if (s == ERR_SUCCESS) {
                // 分析配置描述符,获取端点数据/各端点地址/各端点大小等,更新变量endp_addr和endp_size等
                s = AnalyzeCfgDescr(Com_Buffer, &cfg, &ifc); // 从描述符中分析出HID中断端点的地址
                if (s > 0) {                                 // 找到中断端点数 > 0
                    // 需保存端点信息以便主程序进行USB传输
                    if (ThisUsbDev.DeviceType == DEV_TYPE_KEYBOARD) {

                        printf("USB-Keyboard detected\n");

                        s = ERR_SUCCESS;
                    } else if (ThisUsbDev.DeviceType == DEV_TYPE_MOUSE) {
                        // 为了以后查询鼠标状态,应该分析描述符,取得中断端口的地址,长度等信息

                        printf("USB-Mouse detected\n");

                        s = ERR_SUCCESS;
                    } else {
                        return (ERR_USB_UNSUPPORT); // 不支持的USB设备
                    }

                    printf("CtrlSetUsbConfig x%02x\n", (UINT16)cfg);

                    s = CtrlSetConfig(cfg); // 设置USB设备配置

                    printf("CtrlSetConfig result: x%02x\n", s);

                    if (s == ERR_SUCCESS) {
                        ThisUsbDev.DeviceStatus = ROOT_DEV_SUCCESS;

                        CtrlHIDSetIdle(ThisUsbDev.HIDbIfcNum);

                        printf("Device Ready.\n");
                    }
                    return (s);
                } else // 其他设备
                {
                    return (ERR_USB_UNSUPPORT); // 不支持的USB设备
                }
            }
        }
    }

    printf("InitRootDev Err = x%02X\n", (UINT16)s);

    ThisUsbDev.DeviceStatus = ROOT_DEV_FAILED;
    SetUsbSpeed(1); // 默认为全速
    t++;
    goto USBDevEnum;
}
/*******************************************************************************
 * Function Name  : EnumAllRootDevice
 * Description    : 枚举所有ROOT-HUB端口的USB设备
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
UINT8 EnumAllRootDevice(void) {
    UINT8I s;

    printf("EnumUSBDev\n");

    if (ThisUsbDev.DeviceStatus == ROOT_DEV_CONNECTED) // 刚插入设备尚未初始化
    {
        s = InitRootDevice(); // 初始化/枚举指定HUB端口的USB设备
        if (s != ERR_SUCCESS) {
            return (s);
        }
    }
    return (ERR_SUCCESS);
}

/*******************************************************************************
 * Function Name  : setReport
 * Description    : 发送报表--LED点灯
 * Input          : UINT8 val 点灯键值
 * Output         : None
 * Return         : None
 *******************************************************************************/
UINT8 setReport(UINT8 val) {
    UINT8 s;
    CopySetupReqPkg(SetupHIDSetReport);

    Com_Buffer[0] = val;
    Com_BufferLen = 1;

    s = USBSetupstTransfer(Com_Buffer, &Com_BufferLen); // 执行控制传输
    return (ERR_SUCCESS);
}

/*******************************************************************************
 * Function Name  : InitUSB_Host
 * Description    : 初始化USB主机
 * Input          : None
 * Output         : None
 * Return         : None
 *******************************************************************************/
void InitUSB_Host(void) {
    UINT8 i;
    IE_USB = 0;
    //  LED_CFG = 1;
    //  LED_RUN = 0;
    USB_CTRL = bUC_HOST_MODE;  // 先设定模式
    UHOST_CTRL &= ~bUH_PD_DIS; // 启用主机下拉
    USB_DEV_AD = 0x00;
    UH_EP_MOD = bUH_EP_TX_EN | bUH_EP_RX_EN;
    UH_RX_DMA = (UINT8PV)RxBuffer;
    UH_TX_DMA = (UINT8PV)TxBuffer;
    UH_RX_CTRL = 0x00;
    UH_TX_CTRL = 0x00;
    USB_CTRL = bUC_HOST_MODE | bUC_INT_BUSY; // | bUC_DMA_EN;                     // 启动USB主机及DMA,在中断标志未清除前自动暂停
                                             //  UHUB0_CTRL = 0x00;
                                             //  UHUB1_CTRL = 0x00;
                                             //  UH_SETUP = bUH_SOF_EN;
    USB_INT_FG = 0xFF;                       // 清中断标志
    for (i = 0; i != 2; i++) {
        DisableRootHubPort(); // 清空
    }
    USB_INT_EN = bUIE_TRANSFER | bUIE_DETECT;
    //  IE_USB = 1;                                                                  // 查询方式
}
